using UnityEngine;
using System.Collections;
using System;

namespace DanielSig.Geometry.Orbiter
{
	public class Orbiter2D : Orbiter
	{
		
		public static explicit operator Vector2 (Orbiter2D orbiter)
		{
			return orbiter._direction;
		}
		public static explicit operator Vector3 (Orbiter2D orbiter)
		{
			return (Vector3)orbiter._direction;
		}
		
		public static Orbiter2D operator ++ (Orbiter2D orbiter)
		{
			orbiter.NextAndUpdate ();
			return orbiter;
		}
		public static Orbiter2D operator + (Orbiter2D orbiter, int iterations)
		{
			orbiter = orbiter.Copy () as Orbiter2D;
			if (iterations > 0) {
				for (int i = 0; i < iterations; i++) {
					orbiter.QuickNext ();
				}
			} else {
				for (int i = 0; i > iterations; i--) {
					orbiter.QuickPrevious ();
				}
			}
			orbiter._iterations += iterations;
			orbiter.Update ();
			return orbiter;
		}
		
		public new Vector2 direction {
			get {
				_direction.x = (float)_dirX;
				_direction.y = (float)_dirY;
				return _direction;
			}
		}
		
		private Orbiter2D (float angleSpeed, double gravity, double velX, double velY, double dirX, double dirY, int iterations)
		{
			_angleSpeed = angleSpeed;
			_gravity = gravity;
			_velX = velX;
			_velY = velY;
			_dirX = dirX;
			_dirY = dirY;
			_iterations = iterations;
		}
		
		
		public Orbiter2D ()
		{
			_direction = Vector2.up;
			InitOrbiter ();
		}
		public Orbiter2D (float angleSpeed)
		{
			_direction = Vector2.up;
			_angleSpeed = angleSpeed;
			InitOrbiter ();
		}
		public Orbiter2D (Vector2 direction, float angleSpeed)
		{
			_direction = direction;
			_angleSpeed = angleSpeed;
			InitOrbiter();
		}
		
		public override Orbiter Copy ()
		{
			return new Orbiter2D(_angleSpeed, _gravity, _velX, _velY, _dirX, _dirY, _iterations) as Orbiter;
		}
		
		public override void Next ()
		{
			_velX -= _dirX * _gravity;
			_velY -= _dirY * _gravity;
			_dirX += _velX;
			_dirY += _velY;
			_iterations++;
		}
		public override void Previous ()
		{
			_dirX -= _velX;
			_dirY -= _velY;
			_velX += _dirX * _gravity;
			_velY += _dirY * _gravity;
			_iterations--;
		}
		public override void Update ()
		{
			_direction.x = (float)_dirX;
			_direction.y = (float)_dirY;
		}
		public override void NextAndUpdate ()
		{
			_velX -= _dirX * _gravity;
			_velY -= _dirY * _gravity;
			_dirX += _velX;
			_dirY += _velY;
			_direction.x = (float)_dirX;
			_direction.y = (float)_dirY;
			_iterations++;
		}
		public override void PreviousAndUpdate ()
		{
			_dirX -= _velX;
			_dirY -= _velY;
			_velX += _dirX * _gravity;
			_velY += _dirY * _gravity;
			_direction.x = (float)_dirX;
			_direction.y = (float)_dirY;
			_iterations--;
		}
		
		protected Vector2 _direction;
		
		protected override void QuickNext ()
		{
			_velX -= _dirX * _gravity;
			_velY -= _dirY * _gravity;
			_dirX += _velX;
			_dirY += _velY;
		}
		protected override void QuickPrevious ()
		{
			_velX -= _dirX * _gravity;
			_velY -= _dirY * _gravity;
			_dirX += _velX;
			_dirY += _velY;
		}
		
		protected override void InitOrbiter ()
		{
			base.InitOrbiter ();
			
			_dirX = _direction.x;
			_dirY = _direction.y;
			
			double radians = -_angleSpeed * Mathf.Deg2Rad;
			double cos = Math.Cos(radians);
			double sin = Math.Sin(radians);
			Vector2 prevDir = new Vector2((float)(cos * _dirX - sin * _dirY), (float)(sin * _dirX + cos * _dirY));
			
			Vector2 velocity = _direction - prevDir;
			_velX = velocity.x;
			_velY = velocity.y;
			
			_gravity = ((_direction * 2 - prevDir) - (2 * Vector2.Dot(prevDir, _direction.normalized) * _direction.normalized - prevDir)).magnitude / _direction.magnitude;
		}
	}
}